package server;

import java.util.ArrayList;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

public class PrimeMachine {

	// TreeSet is used to order primes in ascendant order
	private static SortedSet<Integer> knownPrimes = Collections.synchronizedSortedSet(new TreeSet<Integer>());

	public static int getKnownPrimesSize(){
		return knownPrimes.size();		
	}

	static {
		// generate Primes before start.
		// This is a demo version, so the limit for numbers is Integer.MAX_VALUE
		PrimeMachine.generatePrimes(PrimeMachine.MAX_PRIME);
	}
	/**
	 * This is enough to decompose whole Integer range into product of primes
	 * Actually it is 46340,95001 == sqrt(Integer.MAX_VALUE)
	 */
	public final static int MAX_PRIME = 65536; 

	/**
	 * Populates <code>knownPrimes</code> for <i>caching</i> purposes.
	 * And it is <code>synchronized</code> so no one accesses our 
	 * @param upTo - the target value
	 */
	public static long generatePrimes(int upTo){
		System.out.println("PrimeMachine> I generate primes set...");

		int max = MAX_PRIME;
		// Allocating a big
		boolean[] buffer = new boolean[max+1];

		long start = System.currentTimeMillis();
		AtkinSieveArray.sieve(buffer);

		for (int i = 0; i < buffer.length; i++) {
			if(buffer[i]) {
				knownPrimes.add(i);
			}
		}
		
		// clear the reference
		buffer = null;
		
		// Suggest JVM to collect the garbage. big buffer seems like a garbage now
		System.gc();
		long end = System.currentTimeMillis();
		System.out.printf("PrimeMachine> Done in %f seconds!%n", (end - start) / 1000.);
		return end - start;
	}




	/**
	 * Returns the String with results
	 * @param target
	 * @return
	 */
	public static String GetAsPrimes(final int target) {

		String result = "Number " + target;
		if (knownPrimes.contains(target)) {
			return result + " is a prime.";
		}
		int number = target;
		ArrayList<Integer> primes = new ArrayList<Integer>();

		// Safely operate with primes in collection
		nextPlease: while (!knownPrimes.contains(number)){
			for (Integer i : knownPrimes) {
				if (number % i == 0) {
					primes.add(i);
					number /= i;
					continue nextPlease;
				}
			}

			// Exit, because this number is a prime!
			break;
		}
		primes.add(number);

		if (primes.size() == 1){
			return "Number " + number + " is a prime!";
		}
		return result + " is a product of " + primes.toString();
	}





}
